/**
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2007，所有权利保留。
 * 
 * 项目名：	rumba
 * 文件名：	HColumnDef.java
 * 模块说明：	
 * 修改历史：
 * Aug 24, 2007 - lxm - 创建。
 */
package com.hd123.rumba.jsp.table;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;

import com.hd123.rumba.request.JspLib;
import com.hd123.rumba.util.AccessMode;

/**
 * 列定义对象。
 * 
 * @author lxm
 * 
 */
public class HColumnDef {

  /**
   * 构造函数。
   * 
   * @param fieldName
   *          指定字段名。
   * @param caption
   *          指定列标题。
   * @param params
   *          其它参数数组。可用的参数说明参见相应的属性说明。
   */
  public HColumnDef(String fieldName, String caption, Object[] params) {
    super();
    assert fieldName != null;

    this.fieldName = fieldName;
    this.caption = caption;
    this.enumMap = new HashMap<Object, String>();

    this.groupCaption = null;
    this.businessAccessMode = VALUE_BAM_NONE;
    this.permissionId = null;
    this.hidden = Boolean.FALSE;
    this.writable = Boolean.TRUE;
    this.tabStop = Boolean.TRUE;
    this.isRequired = Boolean.FALSE;
    this.width = null;
    this.displayFormat = null;
    this.prefix = null;
    this.suffix = null;
    this.noWrap = Boolean.FALSE;
    this.align = null;
    this.vAlign = null;
    this.onDrawCellJS = null;
    this.onValidateJS = null;
    this.onEnterJS = null;
    this.onExitJS = null;
    this.editor = null;

    for (int i = 0; i < params.length;) {
      String paramName = (String) params[i++];
      Object paramValue = params[i++];
      if ("groupCaption".equals(paramName))
        this.groupCaption = (String) paramValue;
      else if ("businessAccessMode".equals(paramName))
        this.businessAccessMode = (Integer) paramValue;
      else if ("permissionId".equals(paramName))
        this.permissionId = (String) paramValue;
      else if ("hidden".equals(paramName))
        this.hidden = (Boolean) paramValue;
      else if ("writable".equals(paramName))
        this.writable = (Boolean) paramValue;
      else if ("tabStop".equals(paramName))
        this.tabStop = (Boolean) paramValue;
      else if ("isRequired".equals(paramName))
        this.isRequired = (Boolean) paramValue;
      else if ("width".equals(paramName))
        this.width = (Integer) paramValue;
      else if ("displayFormat".equals(paramName))
        this.displayFormat = (String) paramValue;
      else if ("prefix".equals(paramName))
        this.prefix = (String) paramValue;
      else if ("suffix".equals(paramName))
        this.suffix = (String) paramValue;
      else if ("noWrap".equals(paramName))
        this.noWrap = (Boolean) paramValue;
      else if ("align".equals(paramName))
        this.align = (String) paramValue;
      else if ("vAlign".equals(paramName))
        this.vAlign = (String) paramValue;
      else if ("onDrawCellJS".equals(paramName))
        this.onDrawCellJS = (String) paramValue;
      else if ("onValidateJS".equals(paramName))
        this.onValidateJS = (String) paramValue;
      else if ("onEnterJS".equals(paramName))
        this.onEnterJS = (String) paramValue;
      else if ("onExitJS".equals(paramName))
        this.onExitJS = (String) paramValue;
    }
  }

  private HTable table;
  private String fieldName;
  private String caption;
  private String groupCaption;
  private Integer businessAccessMode;
  private String permissionId;
  private Boolean hidden;
  private Boolean writable;
  private Boolean tabStop;
  private Boolean isRequired;
  private Integer width;
  private String displayFormat;
  private HashMap<Object, String> enumMap;
  private String prefix;
  private String suffix;
  private Boolean noWrap;
  private String align;
  private String vAlign;
  private String onDrawCellJS;
  private String onValidateJS;
  private String onEnterJS;
  private String onExitJS;
  private HEditor editor;

  /** 属性{@link #businessAccessMode}的取值。表示无约束。 */
  public static final Integer VALUE_BAM_NONE = new Integer(0);
  /** 属性{@link #businessAccessMode}的取值。表示最高访问权限为只读。 */
  public static final Integer VALUE_BAM_READONLY = new Integer(1);
  /** 属性{@link #businessAccessMode}的取值。表示总是处于可读写状态，将忽略权限和formState。 */
  public static final Integer VALUE_BAM_ALWAYSWRITABLE = new Integer(2);

  /** 属性：所属表格控件。 */
  public HTable getTable() {
    return table;
  }

  void setTable(HTable table) {
    this.table = table;
  }

  /** 属性：标题 */
  public String getCaption() {
    return caption;
  }

  public void setCaption(String caption) {
    this.caption = caption;
  }

  /** 属性：字段名，只读。 */
  public String getFieldName() {
    return fieldName;
  }

  void setFieldName(String fieldName) {
    this.fieldName = fieldName;
  }

  /**
   * 属性：水平对齐方式。取值参见CSS属性"text-align"。允许null，表示使用系统默认对齐方式。<br>
   * 对应参数名"align"，下一个参数为取值，类型为String。
   */
  public String getAlign() {
    return align;
  }

  public void setAlign(String align) {
    this.align = align;
  }

  /**
   * 属性：业务访问模式。<br>
   * 对应的参数名"businessAccessMode"。下一个参数为取值，数据类型为Integer，可用的取值参见"VALUE_BAM_"开头的常量的说明。<br>
   * 对应参数名"businessAccessMode"，下一个参数为取值，类型为Integer。
   */
  public Integer getBusinessAccessMode() {
    return businessAccessMode;
  }

  public void setBusinessAccessMode(Integer businessAccessMode) {
    this.businessAccessMode = businessAccessMode;
  }

  /**
   * 属性：显示格式字符串。允许null，这时的显示格式为系统默认。默认为null。<br>
   * 对应参数名"displayFormat"，下一个参数为取值，类型为String。
   */
  public String getDisplayFormat() {
    return displayFormat;
  }

  public void setDisplayFormat(String displayFormat) {
    this.displayFormat = displayFormat;
  }

  /** 枚举映射表。key=取值；value=显示字符串。 */
  public HashMap<Object, String> getEnumMap() {
    return enumMap;
  }

  public void setEnumMap(HashMap<Object, String> enumMap) {
    this.enumMap = enumMap;
  }

  /**
   * 属性：标题组。对于相邻的两个列如果该属性指定相同的取值，则会显示一个标题组。null或空字符串，表示无组标题。默认为null。<br>
   * 对应参数名"groupCaption"，下一个参数为取值，类型为String。
   */
  public String getGroupCaption() {
    return groupCaption;
  }

  public void setGroupCaption(String groupCaption) {
    this.groupCaption = groupCaption;
  }

  /**
   * 属性：是否隐藏。默认为false。<br>
   * 对应参数名"hidden"，下一个参数为取值，类型为Boolean。
   */
  public Boolean getHidden() {
    return hidden;
  }

  public void setHidden(Boolean hidden) {
    this.hidden = hidden != null ? hidden : Boolean.FALSE;
  }

  /**
   * 属性：是否必填字段。默认为false。<br>
   * 对应参数名"isRequired"，下一个参数为取值，类型为Boolean。
   */
  public Boolean getIsRequired() {
    return isRequired;
  }

  public void setIsRequired(Boolean isRequired) {
    this.isRequired = isRequired != null ? isRequired : Boolean.FALSE;
  }

  /**
   * 属性：指定列数据显示时是否禁止折行。默认为false。<br>
   * 对应参数名"noWrap"，下一个参数为取值，类型为Boolean。
   */
  public Boolean getNoWrap() {
    return noWrap;
  }

  public void setNoWrap(Boolean noWrap) {
    this.noWrap = noWrap != null ? noWrap : Boolean.FALSE;
  }

  /**
   * 属性：当画出单元格时被触发的事件。进入时tdCell中已经按照默认方式填充内容。<br>
   * 对应参数名"onDrawCellJS"，下一个参数为取值，类型为String。
   * 
   * <br>
   * 通常情况下这应该指向一个javascript的函数名，该函数将在事件发生时被触发。具体说明如下（以下说明为js语法）：<br>
   * 
   * <br>
   * <code>function (tdCell, record)</code><br>
   * 参数：
   * <dl>
   * <dd>HTMLDOMElement tdCell - 对应单元格的页面元素&lt;td&gt;。
   * <dd>RDataset.Record record - 对应的数据记录。
   * </dl>
   */
  public String getOnDrawCellJS() {
    return onDrawCellJS;
  }

  public void setOnDrawCellJS(String onDrawCellJS) {
    this.onDrawCellJS = onDrawCellJS;
  }

  /**
   * 属性：当进入单元格时触发的事件。<br>
   * 对应参数名"onEnterJS"，下一个参数为取值，类型为String。
   * 
   * <br>
   * 通常情况下这应该指向一个javascript的函数名，该函数将在事件发生时被触发。具体说明如下（以下说明为js语法）：<br>
   * 
   * <br>
   * <code>function (row)</code><br>
   * 参数：
   * <dl>
   * <dd>RTable.Row row - 对应的数据行对象。
   * </dl>
   */
  public String getOnEnterJS() {
    return onEnterJS;
  }

  public void setOnEnterJS(String onEnterJS) {
    this.onEnterJS = onEnterJS;
  }

  /**
   * 属性：当离开单元格时触发的事件。<br>
   * 对应参数名"onExitJS"，下一个参数为取值，类型为String。
   * 
   * <br>
   * 通常情况下这应该指向一个javascript的函数名，该函数将在事件发生时被触发。具体说明如下（以下说明为js语法）：<br>
   * 
   * <br>
   * <code>function (row)</code><br>
   * 参数：
   * <dl>
   * <dd>RTable.Row row - 对应的数据行对象。
   * </dl>
   */
  public String getOnExitJS() {
    return onExitJS;
  }

  public void setOnExitJS(String onExitJS) {
    this.onExitJS = onExitJS;
  }

  /**
   * 属性：当单元格输入数据确认时被触发的事件。<br>
   * 对应参数名"onValidateJS"，下一个参数为取值，类型为String。
   * 
   * <br>
   * 通常情况下这应该指向一个javascript的函数名，该函数将在事件发生时被触发。具体说明如下（以下说明为js语法）：<br>
   * 
   * <br>
   * <code>function (value, row, isValid)</code><br>
   * 参数：
   * <dl>
   * <dd>RParam&lt;Object&gt; value -
   * 对应的数据值，传入传出参数。可以在必要的时候修改数据值，传出的数据值将会被写入对应数据（集）。
   * <dd>RTable.Row row - 对应的当前数据行对象。
   * <dd>RParam&lt;Boolean&gt; isValid - 传入传出参数，是否是有效数据，默认为true。
   * </dl>
   */
  public String getOnValidateJS() {
    return onValidateJS;
  }

  public void setOnValidateJS(String onValidateJS) {
    this.onValidateJS = onValidateJS;
  }

  /**
   * 属性：显示前缀。将被加在显示字符串的前面。允许null，表示无前缀。默认为null。<br>
   * 对应参数名"prefix"，下一个参数为取值，类型为String。
   */
  public String getPrefix() {
    return prefix;
  }

  public void setPrefix(String prefix) {
    this.prefix = prefix;
  }

  /**
   * 属性：显示后缀。将被加在显示字符串的后面。允许null，表示无后缀。默认为null。<br>
   * 对应参数名"suffix"，下一个参数为取值，类型为String。
   */
  public String getSuffix() {
    return suffix;
  }

  public void setSuffix(String suffix) {
    this.suffix = suffix;
  }

  /**
   * 属性：是否光标跳转停留。默认为true。<br>
   * 对应参数名"tabStop"，下一个参数为取值，类型为Boolean。
   */
  public Boolean getTabStop() {
    return tabStop;
  }

  public void setTabStop(Boolean tabStop) {
    this.tabStop = tabStop != null ? tabStop : Boolean.TRUE;
  }

  /**
   * 属性：垂直对齐方式。取值参见CSS属性"vertical-align"。允许null，表示使用系统默认对齐方式。<br>
   * 对应参数名"vAlign"，下一个参数为取值，类型为String。
   */
  public String getVAlign() {
    return vAlign;
  }

  public void setVAlign(String align) {
    vAlign = align;
  }

  /**
   * 预定义列宽，单位：像素。null，表示自动宽度，默认为null。<br>
   * 对应参数名"width"，下一个参数为取值，类型为Integer。
   */
  public Integer getWidth() {
    return width;
  }

  public void setWidth(Integer width) {
    this.width = width;
  }

  /** 属性：指定编辑器，允许null，表示使用系统默认编辑器。默认为null。s */
  public HEditor getEditor() {
    return editor;
  }

  public void setEditor(HEditor editor) {
    this.editor = editor;
  }

  /**
   * 属性：对应字段权限。<br>
   * 对应的参数名"permissionId"。下一个参数为取值，数据类型为String。
   */
  public String getPermissionId() {
    return permissionId;
  }

  public void setPermissionId(String permissionId) {
    this.permissionId = permissionId;
  }

  /**
   * 属性：是否可被编辑。默认为true。<br>
   * 对应参数名"writable"，下一个参数为取值，类型为Boolean。
   */
  public Boolean getWritable() {
    return writable;
  }

  public void setWritable(Boolean writable) {
    this.writable = writable == null ? Boolean.TRUE : writable;
  }

  /**
   * 输出Javascript字符串。
   * 
   * @param formState
   *          输出Javascript字符串。
   * @return
   */
  public String toJS(String formState) {
    StringBuffer sb = new StringBuffer();

    sb.append("var colDef = ");
    sb.append(this.table.getTable_VarJS());
    sb.append(".appendColumnDef({");
    sb.append(this.toJS_JSON(formState));
    sb.append("});\n");

    if (this.enumMap != null && this.enumMap.size() > 0)
      sb.append(this.toJS_Enum());
    
    if (this.editor != null)
      sb.append(this.editor.toJS());

    return sb.toString();
  }

  private String toJS_JSON(String formState) {
    StringBuffer sb = new StringBuffer();
    if (this.fieldName != null)
      this.toJS_JSON_prop(sb, "fieldName", JspLib.toJSConst(this.fieldName));
    if (this.caption != null)
      this.toJS_JSON_prop(sb, "caption", JspLib.toJSConst(this.caption));
    if (this.groupCaption != null)
      this.toJS_JSON_prop(sb, "groupCaption", JspLib.toJSConst(this.groupCaption));
    assert this.hidden != null;
    this.toJS_JSON_prop(sb, "hidden", JspLib.toJSConst(this.hidden));
    assert this.writable != null;
    this.toJS_JSON_prop(sb, "writable",
        this.getAccessMode(formState) == AccessMode.READWRITE ? "true" : "false");
    assert this.tabStop != null;
    this.toJS_JSON_prop(sb, "tabStop", JspLib.toJSConst(this.tabStop));
    assert this.isRequired != null;
    this.toJS_JSON_prop(sb, "isRequired", JspLib.toJSConst(this.isRequired));
    if (this.width != null)
      this.toJS_JSON_prop(sb, "width", JspLib.toJSConst(this.width));
    if (this.displayFormat != null)
      this.toJS_JSON_prop(sb, "displayFormat", JspLib.toJSConst(this.displayFormat));
    if (this.prefix != null)
      this.toJS_JSON_prop(sb, "prefix", JspLib.toJSConst(this.prefix));
    if (this.suffix != null)
      this.toJS_JSON_prop(sb, "suffix", JspLib.toJSConst(this.suffix));
    assert this.noWrap != null;
    this.toJS_JSON_prop(sb, "noWrap", JspLib.toJSConst(this.noWrap));
    if (this.align != null)
      this.toJS_JSON_prop(sb, "align", JspLib.toJSConst(this.align));
    if (this.vAlign != null)
      this.toJS_JSON_prop(sb, "vAlign", JspLib.toJSConst(this.vAlign));
    if (this.onDrawCellJS != null && !"".equals(this.onDrawCellJS.trim()))
      this.toJS_JSON_prop(sb, "onDrawCell", this.onDrawCellJS);
    if (this.onValidateJS != null && !"".equals(this.onValidateJS.trim()))
      this.toJS_JSON_prop(sb, "onValidate", this.onValidateJS);
    if (this.onEnterJS != null && !"".equals(this.onEnterJS.trim()))
      this.toJS_JSON_prop(sb, "onEnter", this.onEnterJS);
    if (this.onExitJS != null && !"".equals(this.onExitJS.trim()))
      this.toJS_JSON_prop(sb, "onExit", this.onExitJS);
    return sb.toString();
  }

  private void toJS_JSON_prop(StringBuffer sb, String propName, String propValue) {
    if (sb.length() > 0)
      sb.append(", ");
    sb.append(propName + ": " + propValue);
  }

  private int getAccessMode(String formState) {
    int mode = AccessMode.READWRITE;
    if (VALUE_BAM_ALWAYSWRITABLE.equals(this.businessAccessMode))
      mode = AccessMode.READWRITE;
    else {
      if (this.permissionId == null)
        mode = AccessMode.READWRITE;
      else {
        mode = AccessMode.INVISIBLE;
        AccessMode am = (AccessMode) this.table.getDd().get("am");
        if (am != null) {
          mode = am.getAccessMode(this.permissionId);
          if (mode == -1)
            mode = AccessMode.INVISIBLE;
        }
      }

      if (JspLib.VALUE_FORMSTATE_EDIT.equals(formState)
          || JspLib.VALUE_FORMSTATE_CREATE.equals(formState))
        mode = Math.min(mode, AccessMode.READWRITE);
      else
        mode = Math.min(mode, AccessMode.READONLY);

      if (VALUE_BAM_READONLY.equals(this.businessAccessMode))
        mode = Math.min(mode, AccessMode.READONLY);
    }

    if (!this.writable.booleanValue())
      mode = Math.min(mode, AccessMode.READONLY);
    return mode;
  }

  private String toJS_Enum() {
    StringBuffer sb = new StringBuffer();
    Iterator keys = this.enumMap.keySet().iterator();
    while (keys.hasNext()) {
      Object key = keys.next();
      sb.append("colDef.enumMap[");
      if (key instanceof String)
        sb.append(JspLib.toJSConst((String) key));
      else if (key instanceof Integer)
        sb.append(JspLib.toJSConst((Integer) key));
      else if (key instanceof Boolean)
        sb.append(JspLib.toJSConst((Boolean) key));
      else if (key instanceof BigDecimal)
        sb.append(JspLib.toJSConst((BigDecimal) key));
      else if (key instanceof Date)
        sb.append(JspLib.toJSConst((Date) key));
      else
        continue;
      sb.append("] = ");
      String value = this.enumMap.get(key);
      sb.append(JspLib.toJSConst(value) + ";\n");
    }
    return sb.toString();
  }

}
